package dao

import (
	"context"
	"fmt"
	"gitee.com/hxchjm/go-admin/internal/models"
	"gitee.com/hxchjm/go-admin/pkg/db"
	"gitee.com/hxchjm/go-admin/pkg/jwtauth"
	"github.com/hxchjm/log"
	"github.com/jinzhu/copier"
	"github.com/pkg/errors"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type SysUser struct {
	models.SysUser
}

//func (*SysUser) TableName() string {
//	return "sys_user"
//	//return fmt.Sprintf("%s.sys_user", db.GetDBName())
//}

// Encrypt 加密
func (e *SysUser) Encrypt() (err error) {
	if e.Password == "" {
		return
	}

	var hash []byte
	if hash, err = bcrypt.GenerateFromPassword([]byte(e.Password), bcrypt.DefaultCost); err != nil {
		return
	} else {
		e.Password = string(hash)
		return
	}
}

func (e *SysUser) BeforeCreate(_ *gorm.DB) error {
	return e.Encrypt()
}

func (e *SysUser) BeforeUpdate(_ *gorm.DB) error {
	var err error
	if e.Password != "" {
		err = e.Encrypt()
	}
	return err
}

func (e SysUser) AfterFind(_ *gorm.DB) error {
	e.DeptIds = []int{e.DeptId}
	e.PostIds = []int{e.PostId}
	e.RoleIds = []int{e.RoleId}
	return nil
}

func (e SysUser) GetList(ctx context.Context, req *models.ReqUserGetPage) ([]SysUser, int64, error) {
	var list []SysUser
	var count int64
	if req.Size == 0 {
		req.Size = 20
	}
	if err := db.GetDB().Debug().Model(e).Preload("Dept").Limit(req.Size).Offset((req.Page-1)*req.Size).
		Find(&list, req).Count(&count).Error; err != nil {
		log.Errorf(ctx, "SysUser GetList err (%+v)", err)
		return nil, 0, err
	}
	return list, count, nil
}

func (e SysUser) Get(ctx context.Context) (*SysUser, error) {
	var user SysUser
	if err := db.GetDB().Model(e).Where(e).First(&user).Error; err != nil {
		log.Errorf(ctx, "SysRole Get err (%+v)", err)
		return nil, err
	}
	return &user, nil
}

func (e SysUser) Insert(ctx context.Context) error {
	e.CreateBy = jwtauth.GetClaims(ctx).UID
	var count int64
	if err := db.GetDB().Model(e).Where("username = ?", e.Username).Count(&count).Error; err != nil {
		log.Errorf(ctx, "SysUser Insert err (%+v)", err)
		return err
	}
	if count > 0 {
		err := fmt.Errorf("用户名已存在！")
		log.Errorf(ctx, "db error: %s", err)
		return err
	}

	if err := db.GetDB().Create(&e).Error; err != nil {
		log.Errorf(ctx, "SysUser create err (%+v)", err)
		return err
	}
	return nil
}

func (e SysUser) Update(ctx context.Context) error {
	e.UpdateBy = jwtauth.GetClaims(ctx).UID
	var model models.SysUser
	tx := db.GetDB().Model(e).First(&model, e.UserId)
	if tx.Error != nil {
		log.Errorf(ctx, "get db err (%+v)", tx.Error)
		return tx.Error
	}
	if tx.RowsAffected == 0 {
		return fmt.Errorf("无权更新该数据")
	}

	update := db.GetDB().Model(&model).Where("user_id = ?", &e.UserId).Omit("password", "salt").Updates(&e)
	if err := update.Error; err != nil {
		log.Errorf(ctx, "db error: %s", err)
		return err
	}
	if update.RowsAffected == 0 {
		err := fmt.Errorf("update userinfo error")
		log.Errorf(ctx, "db update error")
		return err
	}
	return nil
}

// Delete 删除SysRole
func (e SysUser) Delete(ctx context.Context, ids []int) error {
	var err error
	var data models.SysUser

	tx := db.GetDB().Model(&data).Delete(&data, ids)
	if err = tx.Error; err != nil {
		log.Errorf(ctx, "Error found in  RemoveSysUser : %s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		return fmt.Errorf("无权删除该数据")
	}
	return nil
}

// UpdateStatus 更新用户状态
func (e SysUser) UpdateStatus(ctx context.Context) error {
	var err error
	var model models.SysUser
	tx := db.GetDB().First(&model, e.UserId)
	if err = tx.Error; err != nil {
		log.Errorf(ctx, "Service UpdateSysUser error: %s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		return errors.New("无权更新该数据")

	}
	err = db.GetDB().Table(e.TableName()).Where("user_id =? ", e.UserId).Updates(e).Error
	if err != nil {
		log.Errorf("Service UpdateSysUser error: %s", err)
		return err
	}
	return nil
}

// ResetPwd 重置用户密码
func (e SysUser) ResetPwd(ctx context.Context) error {
	var err error
	var model models.SysUser
	tx := db.GetDB().First(&model, e.UserId)
	if err = tx.Error; err != nil {
		log.Errorf(ctx, "At Service ResetSysUserPwd error: %s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		return errors.New("无权更新该数据")
	}
	pwd := e.Password
	_ = copier.CopyWithOption(&e, model, copier.Option{IgnoreEmpty: true})
	e.Password = pwd
	err = db.GetDB().Debug().Omit("username", "nick_name", "phone", "role_id", "avatar", "sex").Save(&e).Error
	if err != nil {
		log.Errorf(ctx, "At Service ResetSysUserPwd error: %s", err)
		return err
	}
	return nil
}

// Remove 删除SysUser
func (e *SysUser) Remove(ctx context.Context) error {
	var err error
	var data models.SysUser

	tx := db.GetDB().Model(&data).Delete(&data, e.UserId)
	if err = tx.Error; err != nil {
		log.Errorf(ctx, "Error found in  RemoveSysUser : %s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		return errors.New("无权删除该数据")
	}
	return nil
}

// UpdatePwd 修改SysUser对象密码
func (e *SysUser) UpdatePwd(ctx context.Context, oldPassword, newPassword string) error {
	var err error
	id := jwtauth.GetClaims(ctx).UID
	if newPassword == "" {
		return nil
	}
	c := &models.SysUser{}

	err = db.GetDB().Model(c).Select("UserId", "Password", "Salt").
		First(c, id).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("无权更新该数据")
		}
		log.Errorf(ctx, "db error: %s", err)
		return err
	}
	var ok bool
	//校验密码
	if err = bcrypt.CompareHashAndPassword([]byte(c.Password), []byte(oldPassword)); err != nil {
		log.Errorf(ctx, "CompareHashAndPassword error, %s", err.Error())
		return err
	}

	if !ok {
		err = errors.New("incorrect Password")
		log.Warnf(ctx, "user[%d] %s", id, err.Error())
		return err
	}
	c.Password = newPassword
	tx := db.GetDB().Model(c).Where("user_id = ?", id).
		Select("Password", "Salt").
		Updates(c)
	if err = tx.Error; err != nil {
		log.Errorf("db error: %s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		err = errors.New("set password error")
		log.Warnf("db update error")
		return err
	}
	return nil
}

func (e SysUser) GetProfile(ctx context.Context, user *models.SysUser, roles *[]models.SysRole, posts *[]models.SysPost) error {
	err := db.GetDB().Preload("Dept").First(user, e.UserId).Error
	if err != nil {
		return err
	}
	err = db.GetDB().Debug().Find(roles, user.RoleId).Error
	if err != nil {
		return err
	}
	err = db.GetDB().Debug().Find(posts, user.PostIds).Error
	if err != nil {
		return err
	}

	return nil
}

func (e SysUser) GetUserInfo(ctx context.Context) (user *SysUser, dataScope string, err error) {
	var p = &struct {
		UserId    int
		Password  string
		DeptId    int
		RoleId    int
		RoleKey   string
		DataScope string
	}{}
	if err = db.GetDB().WithContext(ctx).Model(e).Table("sys_user").
		Select("sys_user.user_id", "sys_user.password", "sys_user.dept_id", "sys_role.role_id",
			"sys_role.role_key", "sys_role.data_scope").
		Joins("left join sys_role on sys_role.role_id = sys_user.role_id").
		Where("sys_user.username = ? AND sys_user.deleted_at is null", e.Username).
		Scan(p).Error; err != nil {
		err = errors.New("获取用户数据出错 msg:" + err.Error())
		return
	}
	user = &SysUser{models.SysUser{
		UserId:   p.UserId,
		Password: p.Password,
		RoleId:   p.RoleId,
		DeptId:   p.DeptId,
		PostId:   0,
	}}
	return user, p.DataScope, nil
}
